﻿// Copyright (c) Autofac Project. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using Autofac.Builder;
using Autofac.Core;
using Autofac.Features.Scanning;

namespace Autofac;

/// <summary>
/// Adds registration syntax to the <see cref="ContainerBuilder"/> type.
/// </summary>
[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")]
public static partial class RegistrationExtensions
{
    /// <summary>
    /// Specifies how a type from a scanned assembly is mapped to a keyed service.
    /// </summary>
    /// <param name="registration">Registration to set service mapping on.</param>
    /// <typeparam name="TService">Service type provided by the component.</typeparam>
    /// <param name="serviceKeyMapping">Function mapping types to service keys.</param>
    /// <returns>Registration builder allowing the registration to be configured.</returns>
    public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
        Keyed<TService>(
            this IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle> registration,
            Func<Type, object> serviceKeyMapping)
    {
        return Keyed(registration, serviceKeyMapping, typeof(TService));
    }

    /// <summary>
    /// Specifies how a type from a scanned assembly is mapped to a keyed service.
    /// </summary>
    /// <typeparam name="TLimit">Registration limit type.</typeparam>
    /// <typeparam name="TScanningActivatorData">Activator data type.</typeparam>
    /// <typeparam name="TRegistrationStyle">Registration style.</typeparam>
    /// <param name="registration">Registration to set service mapping on.</param>
    /// <param name="serviceType">Service type provided by the component.</param>
    /// <param name="serviceKeyMapping">Function mapping types to service keys.</param>
    /// <returns>Registration builder allowing the registration to be configured.</returns>
    public static IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle>
        Keyed<TLimit, TScanningActivatorData, TRegistrationStyle>(
            this IRegistrationBuilder<TLimit, TScanningActivatorData, TRegistrationStyle> registration,
            Func<Type, object> serviceKeyMapping,
            Type serviceType)
        where TScanningActivatorData : ScanningActivatorData
    {
        if (registration == null)
        {
            throw new ArgumentNullException(nameof(registration));
        }

        if (serviceKeyMapping == null)
        {
            throw new ArgumentNullException(nameof(serviceKeyMapping));
        }

        if (serviceType == null)
        {
            throw new ArgumentNullException(nameof(serviceType));
        }

        return registration
            .AssignableTo(serviceType)
            .As(t => new KeyedService(serviceKeyMapping(t), serviceType));
    }
}
